﻿/*
 * RendererBase
 * 
 * initial implementation : 20.10.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Collections.Generic;
using Petra.Gis.Geometry;
using Petra.Gis.DataServices;
using Petra.Gis.DataServices.Metadata;
using Petra.Gis.ReferenceSystemServices.Transformations;
using Petra.Gis.RenderingServices.PlanarBase.Support;

namespace Petra.Gis.RenderingServices.PlanarBase
{
  /// <summary>
  /// renderer is responsible for rendering (drawing, painting) spatial geometry data
  /// to specific output format eg. Image, Bitmap, XML, XAML, SVG, SWF,...
  /// renderer must never throws error when incompatible geometry type is given on input
  /// as it is critical component.
  /// </summary>
  public abstract class RendererBase : KeepRelationWith<LegendNodeBase>
  {

    

    /// <summary>
    /// xaml constructor
    /// </summary>
    protected RendererBase()
    {
      //_style = new RenderStyle();
    }




    public virtual void PrepareToRender()
    {
      if (rendererMap == null || renderStyle == null)
        throw new ApplicationException("incomplete renreder " + this.ToString() + ", cant continue");

    }




    public RenderableLegendNodeBase Parent
    {
      get
      {
        if (__parent != null)
          return (RenderableLegendNodeBase)__parent;

        return null;
      }
    }





    protected FeatureMetadata getMetadata()
    {
      if (Parent != null)
        if (Parent.FeatureSet != null)
          return Parent.FeatureSet.Metadata;

      return null;
    }


    


    protected string _srcSpatialFieldName = "";

    /// <summary>
    /// renderer source geometry field name.
    /// Speficies which geometry field in data source will render through this renderer.
    /// Empty value is valid only when feature that contains only single geometry field
    /// otherwise valid name must be supplied
    /// </summary>
    public string SpatialFieldName
    {
      get 
      {
        if (_srcSpatialFieldName != "")
          return _srcSpatialFieldName;

        FeatureMetadata fm = getMetadata();

        if (fm != null)
        {
          if (fm.GeometryFieldsMetadata.Length == 1)
          {
            _srcSpatialFieldName = fm.GeometryFieldsMetadata[0].Name;
            prepareTransforms();
          }
          else
            _srcSpatialFieldName = "";

          return _srcSpatialFieldName;

        }
        return ""; 
      }


      set
      {
        if (value == "")
          throw new ArgumentException("set SpatialFieldName = null is not allwed");

        //check given name against metadata...
        FeatureMetadata fm = getMetadata();
        if (fm == null)
          throw new InvalidOperationException("FeatureMetadata null, cant set SpatialFieldName");

        foreach (SpatialFieldMetadata gfm in fm.GeometryFieldsMetadata)
          if (gfm.Name.ToLower() == value.ToLower())
          {
            _srcSpatialFieldName = value;
            prepareTransforms();
            return;
          }

        throw new ArgumentException("SpatialFieldName must match FeatureMetadata");
      }

    }





    protected SpatialFieldMetadata getSpatialFieldMetadata(string geomFieldName)
    {

      FeatureMetadata fm = getMetadata();

      if (fm == null)
        return null;

      foreach (SpatialFieldMetadata gfm in fm.GeometryFieldsMetadata)
        if (gfm.Name.ToLower() == geomFieldName.ToLower())
          return gfm;

      return null;
    }




    //protected SpatialFieldMetadata _spatialFieldMetadata;

    /// <summary>
    /// specifies geometry metadata for geometry field that should be rendered by this.
    /// If SpatialFieldName property is initialized with valid name then this property is automatically
    /// filled by valid metadata during initialization.
    /// </summary>
    public SpatialFieldMetadata SpatialFieldMetadata
    {
      get
      {
        if(SpatialFieldName != "")
          return getSpatialFieldMetadata(SpatialFieldName);

        return null;

      }
      //set
      //{
      //  if(value == null)
      //    throw new ArgumentException("SpatialFieldName, null");

      //  _spatialFieldMetadata = value;
      //  //_srcSpatialFieldName = _spatialFieldMetadata.Name;

      //  //try prepare transforms
      //  prepareTransforms();
      //}
    
    }




    //protected MapBase _map;

    //must implement in inherited
    protected MapBase rendererMap
    {
      get
      {
        if (__parent != null)
          if (__parent.Legend != null)
            return __parent.Legend.Map;

        return null;
      }
    }




    //public MapBase Map
    //{
    //  protected get 
    //  {
    //    return rendererMap;
    //  }
    //  set { rendererMap = value; }
    //}




    protected SRSTransform _forwardTransform;
    protected SRSTransform _reverseTransform;




    /// <summary>
    /// method that implements drawing (conversion) of gis geometry into output format geometry
    /// </summary>
    /// <param name="geometry">geometry to render (draw) into map</param>
    //public abstract void RenderGeometry(GeometryBase geometry, SRSTransform ForwardTransform, SRSTransform ReverseTransform);
    public abstract void RenderGeometry(GeometryBase geometry);
      






    /// <summary>
    /// gives info about current renderer capability of rendering specified geometry
    /// each renderer might be specialized to specific geometry type
    /// </summary>
    /// <param name="geometry"></param>
    /// <returns>true when given geometry is renderable</returns>
    public abstract bool IsRenderable(GeometryBase geometry);




    //protected RenderStyle _style;

    //must implement in inherited
    protected abstract RenderStyle renderStyle { get; set; }


    /// <summary>
    /// set get RendererBase display/print style definition
    /// </summary>
    public RenderStyle Style 
    {
      get { return renderStyle; }
      set
      { 
        //todo: style must contain at least few items
        if (value == null)
          throw new ArgumentException("invalid null style", "style");

        renderStyle = value;
      }
    }




    ////projection data coordinates in world units to (paper) map units transformation ;-)
    //protected RenderTransformMatrix _transform;

    ///// <summary>
    ///// set <c>RenderTransformMatrix</c> inherited from Map
    ///// </summary>
    //public RenderTransformMatrix DisplayTransform
    //{
    //  get { return _transform; }
    //}




    //protected DomainGeometryTypeEnum _geometryDomain;

    /// <summary>
    /// get <c>DomainGeometryTypeEnum</c> enumerator that defines
    /// geometry domain for current renderer instance
    /// </summary>
    public abstract DomainGeometryTypeEnum RendererGeometryDomain { get; }




    /// <summary>
    /// must inherit and set to valid geometry type o which is current renderer specialized
    /// </summary>
    //protected abstract System.Type rendererSpecializedGeometryType { get; }





    private string _evaluatorStr;

    /// <summary>
    /// string that can be evaluated by c# code parser/compiler
    /// string can contain named parameters that will be 
    /// replaced during render process with values from data source
    /// eg. "this.DrawPolygon(Color.FromARGB([@barvicka], this.PreparedPoints))"
    /// </summary>
    public virtual string VisualAttributeEvaluator
    {
      get { return _evaluatorStr; }
      set
      {
        if (value != "")
          parseParameters();

        _evaluatorStr = value; 
      }
    }




    protected void parseParameters()
    { }





    //protected VisualAttributes _visualAttributes;

    /// <summary>
    /// returns collection of user defined attribute names
    /// parsed from Renderer and Render.Style
    /// these attributes must exit in data source otherwise
    /// renderer will throw error
    /// value part of this collection holds current Attribute (Field) value(s)
    /// </summary>
    public virtual VisualAttributes VisualAttributes
    { get; set; }





    protected DataVisualAttributes _dataVisualAttributes;

    /// <summary>
    /// this property is automatically set by DataLegendNodeBase
    /// when there is exist VisualAttributes for renderer or style
    /// </summary>
    public DataVisualAttributes DataVisualAttributes
    {
      set { _dataVisualAttributes = value; }
    }





    protected virtual void prepareTransforms()
    {
      if (rendererMap != null && SpatialFieldMetadata != null)
        _forwardTransform = rendererMap.SRSTransformCache.GetTransform(SpatialFieldMetadata.SRS, false);
    }



  }

}
